package zhang.lao.service.console.plugin;

import com.google.common.collect.Lists;
import org.springframework.stereotype.Service;
import zhang.lao.build.mybatis.jdbc.auto.model.WnLnglatGt06;
import zhang.lao.build.mybatis.jdbc.auto.model.WnLnglatGt06Example;
import zhang.lao.build.tool.date.DateUtil;
import zhang.lao.dao.base.WnLnglatGt06Dao;

import javax.annotation.Resource;
import java.util.*;
import java.util.Date;

/**
 * Created by Administrator on 2016/11/14.
 */
@Service
public class WorkSplitTool {
    @Resource
    private WnLnglatGt06Dao wnLnglatGt06Dao;
    /*******需要修改的地方
     * 1.计算面积时默认农机宽度 fukuan=1,1米。这个地方其实需要取农机的幅宽字段;
     * 2.所使用的数据驱动为sql server，如连接oracle 需要修改
     * 3. 使用的sql语句为 select * from 某辆车的数据表  where 时间=某一天；如果要批量处理，这个地方还需要修改
     * 4.不知道数据库表，没有做Insert，只做了控制台输出
     *  System.out.println("当天第"+Index_print+"块农田，起始点："+(int)(wj1.get(0))+"终点"+(int)(wj1.get(wj1.size()-1)));
     *   System.out.println("当天第"+Index_print+"块农田，面积："+area_ev[Index_farm]);
     */
    public List<WorkSplitModel> split(int sbid,String day,double fk)
    {
        //从sql中读取数据
        List<Pnt> pntList =  getSbOneDayData(sbid,day);
        clearExtraData(pntList);

        //首先筛除冗余点
        //clearExtraData;

        int minPts=8;
        int E=25;
        int Npts=pntList.size();
        int[] ptsC=new int[Npts];//标注该点的性质
        List<List> c =  dbscan(pntList, E, minPts,ptsC);

        return getResult(pntList,ptsC,c,fk,sbid);
    }


    private List<WorkSplitModel> getResult(List<Pnt> pts,int[] ptsC,List<List> c,Double fk,int sbid)
    {
        List<WorkSplitModel> list = Lists.newArrayList();

        int Index_print;
        for(int indexFarm=c.size()-1;indexFarm>=0;indexFarm--) {

            List wj1=c.get(indexFarm);
            Collections.sort(wj1);
            int[] dIndex = new int[wj1.size() - 1];
            for (int w = 1; w<=wj1.size() - 1; w++) {
                dIndex[w - 1] = (int) (wj1.get(w)) - (int) (wj1.get(w - 1));
            }

            // int farm_index = c.size()+1;
            List arr1max_Index = get1Index(dIndex);
            int c4 = arr1max_Index.size();
            for (int w = c4-1; w >= 0; w--) {
                try {
                    if (dIndex[(int) (arr1max_Index.get(w))] > 25) {
                        int c5 = (int) (arr1max_Index.get(w)) + 1;
                        int c6 = wj1.size() - 1;
                        if (c5 >= c6) {
                            c.set(indexFarm, wj1.subList(0, c6 - 1));
                        } else {
                            c.add(wj1.subList(c5, c6));
                            c.set(indexFarm, wj1.subList(0, c5 - 1));
                            wj1 = c.get(indexFarm);
                        }
                        //我见ptsC没有用上 就先注视掉  wj1 remove操作没有必要 直接 c.set(indexFarm,wj1.subList(c5, c6)); 就可以替换原来集合的值
                    /*    for (int j = c6; j >= c5; j--) {
                            ptsC[(int) (wj1.get(j))] = farm_index;
                            wj1.remove(j);
                        }*/


                        //   farm_index = farm_index + 1;

                    }
                }catch (Exception e){
                    continue;
                }

            }
        }


        //将农田展开了之后需要重新对农田结果集做处理
        for(int Index_farm=c.size()-1;Index_farm>=0;Index_farm--) {
            List wj1 = c.get(Index_farm);
            //Collections.sort(wj1);
            if (wj1.size() < 20 || (int) (wj1.get(0)) < 10) {
                c.remove(Index_farm);

            }
        }
        double wj_distance;
        double[] area_ev=new double[c.size()];
        for(int Index_farm=0;Index_farm<=c.size()-1;Index_farm++) {
            Index_print = Index_farm + 1;
            List wj1 =  c.get(Index_farm);
            //Collections.sort(wj1);

            Pnt pnt_start=(Pnt)(pts.get((int)(wj1.get(0))));

            Pnt pnt_end=(Pnt)(pts.get((int)(wj1.get(wj1.size()-1))));
            Date startTime =pnt_start.getTime();
            Date endTime=pnt_end.getTime();
            if(startTime.getTime()>endTime.getTime()){
                Date time2 =startTime;
                startTime = endTime;
                endTime = time2;
                pnt_start.setTime(startTime);
                pnt_end.setTime(endTime);
            }
            System.out.println("当天第"+Index_print+"块农田，起始点："+(int)(wj1.get(0))+"终点"+(int)(wj1.get(wj1.size()-1)));
            System.out.println("当天第"+Index_print+"块农田，起始点："+pnt_start.getTime()+"终点"+pnt_end.getTime());
            WorkSplitModel workSplitModel = new WorkSplitModel();
            workSplitModel.setStartTime(pnt_start.getTime());
            workSplitModel.setEndTime(pnt_end.getTime());
            workSplitModel.setMj(area_ev[Index_farm]);
            double distance_all=0;
            for(int j=1;j<=wj1.size()-1;j++)
            {
                int j1=(int)(wj1.get(j));
                int j2=(int)(wj1.get(j-1));
                Pnt pnt_j=(Pnt)(pts.get(j1));
                Pnt pnt_j1=(Pnt)(pts.get(j2));
                wj_distance=getdistance(pnt_j.getLat(),pnt_j.getLon(),pnt_j1.getLat(),pnt_j1.getLon());
                distance_all=distance_all+wj_distance;
            }

            area_ev[Index_farm]=distance_all * fk * 1000 * 0.0015;
            workSplitModel.setMj(area_ev[Index_farm]);
            workSplitModel.setDeviceId(sbid);
            list.add(workSplitModel);
            System.out.println("当天第"+Index_print+"块农田，面积："+workSplitModel.getMj());
        }
        return list;


    }
    private  List get1Index(int[] arr){
        List    maxIndex=Lists.newArrayList();

        int t=0;
        for(int i=0; i<arr.length; i++){
            if(arr[i] > 1){
                maxIndex.add(i);

            }
        }
        return maxIndex;
    }


    public List<Pnt> getSbOneDayData(int deviceId, String day){
        String dateStart = day + " 00:00:00";
        String dateEnd = day + " 23:59:59";
        return getSbData(deviceId, dateStart,dateEnd);
    }
    public List<Pnt> getSbData(int deviceId, String dateStart, String dateEnd){
            List<Pnt> list = Lists.newArrayList();
        WnLnglatGt06Example wnLnglatGt06Example = new WnLnglatGt06Example();
        wnLnglatGt06Example.createCriteria().andCreateTimeGreaterThanOrEqualTo(DateUtil.StringToDate(dateStart)).andCreateTimeLessThanOrEqualTo(DateUtil.StringToDate(dateEnd)).andDeviceIdEqualTo(deviceId);
        wnLnglatGt06Example.setOrderByClause("create_time asc");
            List<WnLnglatGt06> mapList = wnLnglatGt06Dao.selectByExample(wnLnglatGt06Example);
            if (mapList != null && mapList.size() > 0) {
                for (WnLnglatGt06 wnLnglatGt06 : mapList) {
                    Pnt pt=new Pnt();
                    pt.setTime(wnLnglatGt06.getCreateTime());
                    pt.setLon(Double.parseDouble(wnLnglatGt06.getLng()));
                    pt.setLat(Double.parseDouble(wnLnglatGt06.getLat()));
                    pt.setSpeed(wnLnglatGt06.getSpeed().intValue() );
                    pt.setOren(wnLnglatGt06.getOrentation());
                    if((pt.getLat()*pt.getLat() + pt.getLon()*pt.getLon()) > 0.00001 ){
                        list.add(pt);
                    }

                }
        }
        return list;
    }
    private double getdistance(double pointa_lat,double pointa_lon,double pointb_lat,double pointb_lon)
    {
        double distance;
        double pi=Math.PI;
        double wj_a = Math.sin(pointa_lat * pi/ 180) * Math.sin(pointb_lat * pi/ 180);
        double wj_b = Math.cos(pointa_lat * pi/ 180) * Math.cos(pointb_lat * pi / 180) * Math.cos((pointb_lon - pointa_lon) * pi/ 180);
        if((wj_a + wj_b) <= 1 )
            distance = 6371004 *Math.acos(wj_a + wj_b) / 1000;
        else
            distance=0;
        return distance;

    }

    /***
     * 算法描述
     在本算法中，首先最外层循环是将所有点进行遍历
     在遍历时，首先判断某一点，是否为核心，若不是，将其标记为噪声点
     若是，将其加入到C中，并对其进行标记（1,2,3……借以取费不同的簇），将其距离范围内的点加入到neighbourPts中
     迭代
     对neighbourPts每个点进行遍历，若未被访问，对其进行距离判定，并将其返回值加入到neighbourPts中
     在判定该点是否被加入到C中，若没有，将其加入到C中，并且将其进行cluster 标记
     * @param pts  存放经纬度数组信息
     * @param E  平面坐标系 距离阈值
     * @param minPts  判断是否为核心点 ，所需的最小点数
     * @param ptsC    数组点的属性 ，属于农田几号
     */
    private  List<List> dbscan(List<Pnt> pts,int E,int minPts,int[] ptsC)
    {
        List<List> c=Lists.newArrayList();
        int Npts=pts.size();
        boolean[] Pvisit=new boolean[Npts]; // Array to keep track of points that have been visited.
        int nc    = 0;               // Cluster counter.
        for(int i=0;i<Npts-1;i++)
        {
            ptsC[i]=0;
            Pvisit[i]=false;
        }
        //Integer[][] arr=new Integer[100][];
        //C定义的是细胞数组，其中的每一个元素性质大小均可不同
        //ArrayList C1=new ArrayList();
        //ArrayList<Integer> C1 = new ArrayList<Integer>();
        for(int n=0;n<Npts-1;n++)
        {

            if (!Pvisit[n] )                           // If this point not visited yet
            {  Pvisit[n] = true;                       // mark as visited
                ArrayList neighbourPts=new ArrayList();
                //返回以第n个向量为核心的距离为E的圆周内的向量
                regionQuery(pts, n, E,neighbourPts); // and find its neighbours

                if(neighbourPts.size() < minPts )  // Not enough points to form a cluster
                //如果数量不够，讲该函数标记为噪音点
                { ptsC[n] = 0;  }                  // Mark point n as noise.
                else                // Form a cluster...
                {
                    List c1=Lists.newArrayList();
                    //标记非噪声点的数量
                    nc++;    // Increment number of clusters and process neighbourhood.
                    //将该核心点加入到C中
                    //    C[k]=new ArrayList();
                    c1.add(n);    // Initialise cluster Nc with point n
                    //标注该点与哪个核心店密度可达，该店属于cluster Nc
                    ptsC[n] = nc;   // and mark point n as being a member of cluster Nc.
                    int ind = 1;        // Initialise index into neighbourPts array.
                    //将以第n个向量为核心的点的周围的点进行判别
                    // For each point P' in neighbourPts ...
                    //该处为一迭代算法知道与相关的点全都迭代完全为止
                    int nb=0;

                    while (ind <= neighbourPts.size())
                    {
                        nb = (int)(neighbourPts.get(ind-1));
                        //若该点没有被访问，就进行同上判别

                        if ( !Pvisit[nb] )       // If this neighbour has not been visited
                        { Pvisit[nb] = true;   // mark it as visited.

                            // Find the neighbours of this neighbour and if it has
                            // enough neighbours add them to the neighbourPts list
                            ArrayList neighbourPtsP=new ArrayList();
                            regionQuery(pts, nb, E,neighbourPtsP);
                            if (neighbourPtsP.size()>= minPts)
                            { for(Object w:neighbourPtsP)
                            {
                                int w1=(int)(w);
                                neighbourPts.add(w1);
                            }

                            }
                        }
                        // If this neighbour nb not yet a member of any cluster add it to this cluster.
                        //如果这个点没有进行标注，把这个点放进C，并且标记其所在的cluster 的标号
                        if (ptsC[nb]==0 )
                        {
                            c1.add(nb);
                            ptsC[nb] = nc;
                        }

                        ind = ind + 1;  // Increment neighbour point index and process
                        // next neighbour
                    }
                    if(c1.size()>20)
                    {
                        c.add(c1);
                    }
                    else{
                        nc--;
                        for(int j=c1.size()-1;j>=0;j--)
                        {
                            ptsC[(int)(c1.get(j))]=0;


                        }


                    }
		           /*    int end=C1.size()-1;

		           	if(end-start>=20)
		           	{
		           	//arr[Nc].addAll(C1.subList(0, 2));

			               C.add(C1.subList(start, end));

			              // C1.removeAll(C1);
		           	}
		           	else{
		           		Nc--;
		           		for(int j=C1.size()-1;j>=start;j--)
		           		{
		           			ptsC[(int)(C1.get(j))]=0;
		           			C1.remove(j);

		           		}


		           	}
		              */
                }

            }
        }
        return c;
    }


    /***
     * Find indices of all points within distance E of point with index n
     This function could make use of a precomputed distance table to avoid
     repeated distance calculations, however this would require N^2 storage.
     Not a big problem either way if the number of points being clustered is
     small.   For large datasets this function will need to be optimised.
     * @param pts      the dim x Npts array of data points
     * @param n          Index of point of interest
     * @param E         Distance threshold
     * @param neighbours 该函数返回的是每次验证一个点的时候，判断该点是不是距离以内的，返回的是所有点的位置
     */
    private  void regionQuery(List<Pnt> pts,int n,int E,ArrayList neighbours)
    {

        int  Npts=pts.size();
        double dlat;
        double dlon;
        Pnt pnt_n=(Pnt)(pts.get(n));
        for(int i=0;i<Npts-1;i++)
        {

            Pnt pnt_i=(Pnt)(pts.get(i));

            dlat=Math.abs(pnt_n.getLat()-pnt_i.getLat()); //纬度 第二排
            if (dlat>0.009 )
            {continue;}

            dlon=Math.abs(pnt_n.getLon()-pnt_i.getLon());//经度 第一排
            if(dlon>0.001)
            {continue;}

            if(i!=n)
            {
                //用经纬度计算平面坐标系距离

                double distance=getdistance(pnt_n.getLat(),pnt_n.getLon(),pnt_i.getLat(),pnt_i.getLon());
                double dist2=distance*1000;
                if(dist2 < E)
                {
                    neighbours.add(i);

                }

                //邻近点 增加时间的考虑
                //             if dist2 < E
                //                  num1=datenum(time(n,:),'HH:MM:SS');
                //                  num2=datenum(time(i,:),'HH:MM:SS');
                //                  if abs(num1-num2)/6.9444e-004<21
                //                neighbours = [neighbours i];


            }

        }





    }


    private  void clearExtraData(List<Pnt> pts)
    {
        //首先筛除冗余点,得到数据集a0,用sql语句得到
        int  Npts=pts.size();
        for(int i=1;i<=Npts-1;i++)
        {
            Pnt pnt_n1=(Pnt)(pts.get(i));
            Pnt pnt_n2=(Pnt)(pts.get(i-1));
            if(pnt_n1.getLat()==pnt_n2.getLat() && pnt_n1.getLon()==pnt_n2.getLon())
            {
                pts.remove(i);
                Npts--;
                i--;
            }

        }

    }


}
